﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using UT3Bots.UTItems;
using UT3Bots.Communications;
using System.IO;

namespace UT3Bots
{
	public class GameState
	{
		private static readonly TimeSpan CHECK_INTERVAL = new TimeSpan(0, 2, 0);
		
		//States in the current Game
		private List<UTBotOppState> _oppList = new List<UTBotOppState>();
		private List<UTItemPoint> _invList = new List<UTItemPoint>();
		private List<UTNavPoint> _navList = new List<UTNavPoint>();
		private Dictionary<UTIdentifier, UTPlayerScore> _scores = new Dictionary<UTIdentifier, UTPlayerScore>();

		private UTIdentifier _selfId;
		private int _fragLimit;
		private DateTime _lastCheckTime;
		


		#region Properties

		/// <summary>
		/// List of UTBotOppState containing all the Opponent Bots that you can currently see
		/// </summary>
		public List<UTBotOppState> PlayersVisible
		{
			get
			{
				return this._oppList;
			}
		}

		/// <summary>
		/// List of UTInvItem containing all the inventory item pickups that you can currently see
		/// </summary>
		public List<UTItemPoint> ItemsVisible
		{
			get
			{
				return this._invList;
			}
		}

		/// <summary>
		/// List of UTNavPoint containing all the Navigation Points you can currently see
		/// </summary>
		public List<UTNavPoint> NavPointsVisible
		{
			get
			{
				return this._navList;
			}
		}


		/// <summary>
		/// List of UTPlayerScore containing all the bots in the game and their scores
		/// </summary>
		public List<UTPlayerScore> CurrentScores
		{
			get
			{
				return this._scores.Values.ToList<UTPlayerScore>();
			}
		}


		internal int FragLimit
		{
			get
			{
				return this._fragLimit;
			}
			set
			{
				this._fragLimit = value;
			}
		}
		#endregion


		//Constructor
		internal GameState()
		{

		}

		
		internal void UpdateGameState(Queue<Message> MessageQueue)
		{
			while (MessageQueue.Count > 0)
			{
				Message m = (Message)MessageQueue.Dequeue();
				UpdateGameState(m);
			}
		}

		internal void UpdateGameState(Message msg)
		{

			switch (msg.Info)
			{
				case InfoMessage.BEGIN:
					this.Clear();
					break;

				case InfoMessage.END:
					break;

				case InfoMessage.PLAYER_INFO:
					UTBotOppState playerSeen = new UTBotOppState(msg);
					this._oppList.Add(playerSeen);
					break;

				case InfoMessage.SELF_INFO:
					if (this._selfId == null)
					{
						this._selfId = new UTIdentifier(msg.Arguments[0]);
					}
					break;

				case InfoMessage.NAV_INFO:
					UTNavPoint nav = new UTNavPoint(
						new UTIdentifier(msg.Arguments[0]),
						UTVector.Parse(msg.Arguments[1]),
						bool.Parse(msg.Arguments[2])
						);
					this._navList.Add(nav);
					break;

				case InfoMessage.PICKUP_INFO:
					UTItemPoint item = new UTItemPoint(
						new UTIdentifier(msg.Arguments[0]),
						UTVector.Parse(msg.Arguments[1]),
						msg.Arguments[2],
						bool.Parse(msg.Arguments[3]),
						bool.Parse(msg.Arguments[4])
						);
					this._invList.Add(item);
					break;

				case InfoMessage.GAME_INFO:
					break;

				case InfoMessage.SCORE_INFO:
					SetScores(msg);
					if (DateTime.Now > _lastCheckTime + CHECK_INTERVAL)
					{
						RemoveOldScores();
						_lastCheckTime = DateTime.Now;
					}
					break;
			}


		}



		internal void Clear()
		{
			this._oppList.Clear();
			this._invList.Clear();
			this._navList.Clear();
		}


		internal void SetScores(Message msg)
		{
			foreach (string scoreInfo in msg.Arguments)
			{
				string[] info = scoreInfo.Split(Message.MESSAGE_SUBSEPARATOR);

				if (info.Length == 3)
				{
					UTIdentifier id = new UTIdentifier(info[0]);
					int score = (int)float.Parse(info[2]);

					if (this._scores.ContainsKey(id))
					{
						this._scores[id].SetScore(score);
					}
					else
					{
						this._scores.Add(id, new UTPlayerScore(id, info[1], score));
					}
				}
			}
		}



		private void RemoveOldScores()
		{
			//Make sure that we arent holding scores for bots that have disconnected
			List<UTPlayerScore> oldScores = new List<UTPlayerScore>();

			foreach (UTPlayerScore score in this._scores.Values)
			{
				if (score.LastUpdated < DateTime.Now - CHECK_INTERVAL)
				{
					oldScores.Add(score);
				}
			}

			foreach (UTPlayerScore score in oldScores)
			{
				this._scores.Remove(score.Id);
			}
		}




		internal void PrintScores(TextWriter output)
		{
			string idTitle = "ID";
			string nameTitle = "Name";
			string scoreTitle = "Score";

			output.WriteLine("\n\n" + new string('-', 65) + "\n");
			output.WriteLine("End Of Game Scores Were:\n");
			output.WriteLine(idTitle.PadRight(30) + " " + nameTitle.PadRight(30) + " " + scoreTitle);

			IOrderedEnumerable<KeyValuePair<UTIdentifier, UTPlayerScore>> scores = this._scores.OrderBy(score => score.Value.Score);

			foreach (KeyValuePair<UTIdentifier, UTPlayerScore> keyValue in scores)
			{
				output.WriteLine(keyValue.Value.ToString() + (keyValue.Key == this._selfId ? "     *YOU*" : ""));
			}
		}



		/// <summary>
		/// Get a Bot's name from a given Id
		/// </summary>
		/// <param name="BotID">The Id to search for</param>
		/// <returns>The name of the bot with this id, or "" if the BotId isnt in the game</returns>
		public string GetBotNameFromID(UTIdentifier BotId)
		{
			if (this._scores.ContainsKey(BotId))
			{
				return this._scores[BotId].Name;
			}

			return "";
		}


		/// <summary>
		/// Check to see if a specified Id is from a Bot
		/// </summary>
		/// <param name="Id">The Id to check</param>
		/// <returns>True if the Id belongs to a Bot, false otherwise</returns>
		public bool IsBot(UTIdentifier Id)
		{
			return this._scores.ContainsKey(Id);
		}

	}
}
